<?php
namespace App\Http\Logic;

use App\Models\SwtPledge;
use App\Models\SwtPledgeOrder;
use App\Models\SwtPledgesRule;
use App\Models\SwtTradeAccountLog;
use App\Models\SwtTradeAddress;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;

class PledgeLogic extends BaseLogic
{
    public static function indexLists() : array
    {
        $lang = App::getLocale();

        $list = SwtPledge::with(['coin','rules'])
            -> where('status',1)
            -> where('show_index',1)
            -> orderBy('sort', 'desc')
            -> get();

        $list -> map(function($item) use ($lang) {
            $item -> icon = config('app.url').'/storage/'.$item->coin->icon;

            $item -> lock_year = ceil($item -> lock_day / 365);

            if($item -> rules) {
                $item -> rules -> map( function ($rule) {
                    $rule -> year_rate = (float) $rule -> year_rate;
                });
            }

            if($lang == 'en') {
                $item -> name = $item -> name_for_en;
                $item -> desc = $item -> desc_for_en;
            }

            Arr::except($item, ['coin']);

            return $item;
        });

        return $list -> toArray();
    }

    public static function getListsByCoin() : array
    {
        $lang = App::getLocale();

        $list = SwtPledge::with(['coin'])
            -> where('status',1)
            -> where('show_index',1)
            -> orderBy('sort', 'desc')
            -> get();

        $list -> map(function($item) use ($lang) {
            $item -> icon = config('app.url').'/storage/'.$item->coin->icon;

            Arr::except($item, ['coin']);

            $item -> max_rate = SwtPledgesRule::where('pledge_id', $item -> id) -> max('year_rate');

            $item -> min_rate = SwtPledgesRule::where('pledge_id', $item -> id) -> min('year_rate');

            $item -> max_rate = (float) $item -> max_rate;
            $item -> min_rate = (float) $item -> min_rate;

            if($lang == 'en') {
                $item -> name = $item -> name_for_en;
                $item -> desc = $item -> desc_for_en;
            }

            return $item;
        });

        return $list -> toArray();
    }

    public static function getInfo(int $id) : array {
        $lang = App::getLocale();

        $info = SwtPledge::with(['coin','rules']) -> find($id);

        if(!$info) {
            return [];
        }

        Arr::except($info, ['coin']);

        $info -> icon = config('app.url').'/storage/'.$info->coin->icon;

        $info -> max_rate = SwtPledgesRule::where('pledge_id', $info -> id) -> max('year_rate');

        $info -> min_rate = SwtPledgesRule::where('pledge_id', $info -> id) -> min('year_rate');

        if($lang == 'en') {
            $info -> name = $info -> name_for_en;
            $info -> desc = $info -> desc_for_en;
        }

        $info -> max_rate = (float) $info -> max_rate;

        $info -> min_rate = (float) $info -> min_rate;

        return $info -> toArray();
    }

    public static function createOrder(int $user_id, int $rule_id, string $pledge_num) : array | bool
    {
        if($pledge_num < 100) {
            self::$error = __("validate-message.pledge_num_error");
            return false;
        }

        $pledge = SwtPledgesRule::with('pledge') -> find($rule_id);

        $trade_info = SwtTradeAddress::where('user_id', $user_id)
            -> where('coin_id', $pledge -> pledge -> coin_id)
            -> where('type', 1)
            -> first();

        if($pledge_num > $trade_info -> total){
            self::$error = __("coin-type.funding_insufficient_balance");
            return false;
        }

        DB::beginTransaction();
        try {
            $trade_info -> total = bcsub($trade_info -> total, $pledge_num, 5);

            $trade_info -> lock_total = bcadd($pledge_num, $trade_info -> lock_total, 5);

            $res = $trade_info -> save();

            if(!$res) {
                throw new \Exception(__("coin-type.update_balance_error"));
            }

            $data = [
                'user_id'    => $user_id,
                'trade_id'   => $trade_info->id,
                'pledge_id'  => $pledge->pledge->id,
                'rule_id'    => $rule_id,
                'order_no'   => createSn(12),
                'total'      => $pledge_num,
                'year_rate'  => $pledge->year_rate,
                'lock_day'   => $pledge->lock_day,
                'status'     => 1,
                'start_time' => date('Y-m-d H:i:s', time()),
                'end_time'   => date('Y-m-d H:i:s', strtotime('+ ' . $pledge->lock_day . 'days'))
            ];

            $order = SwtPledgeOrder::create($data);

            if(!$order) {
                throw new \Exception(__("order.order_create_fail"));
            }

            SwtTradeAccountLog::create([
                "address_id"    => $trade_info -> id,
                "user_id"       => $user_id,
                "source_type"   => 4,
                "source_id"     => $order -> id,
                "text"          => "{pledge_order_dec}" . $pledge_num,
                "change_type"   => 1,
                'change_amount' => $pledge_num
            ]);

            Db::commit();
            return [
                'order_no' => $data['order_no'],
                'order_id' => $order -> id
            ];
        } catch (\Exception $e) {
            Db::rollback();
            self::$error = $e->getMessage();
            return false;
        }
    }

    public static function getOrderList(int $user_id, string $status, int $page_no, int $page_size) : array
    {
        $where = [
          ['user_id', '=', $user_id]
        ];

        if(!empty($status)) {
            $where[] = ['status', '=', $status];
        }

        $orders = SwtPledgeOrder::where($where) -> orderBy('id','desc');

        $pages = $orders -> paginate(
            perPage: $page_size,
            columns: ['*'],
            pageName: 'pageNo',
            page: $page_no
        );

        $list = $orders -> get();

        $arr = [
            0 => __("coin-type.wait_effective"),
            1 => __("coin-type.effective"),
            2 => __("coin-type.redeemed"),
            3 => __("coin-type.expired"),
            4 => __("coin-type.cancel")
        ];

        $list -> map (function ($item) use ($arr) {
            $item -> status_desc = $arr[$item -> status];
        });

        return [
            'list'        => $list,
            'pageNo'      => $pages->currentPage(),
            'pageSize'    => $pages->perPage(),
            'count'       => $pages->total(),
            'noMore'      => !$pages->hasMorePages(),
        ];
    }

    public static function getOrderInfo(int $order_id) : array|bool
    {
        $order = SwtPledgeOrder::with('trade') -> find($order_id);

        if(!$order) {
            self::$error = __("order.order_not_found");
            return false;
        }

        $status_arr = [
            0 => __("coin-type.wait_effective"),
            1 => __("coin-type.effective"),
            2 => __("coin-type.redeemed"),
            3 => __("coin-type.expired"),
            4 => __("coin-type.cancel")
        ];

        $order -> status_desc = $status_arr[$order -> status];

        $order -> coin_name = $order -> trade -> chain_name;

        $order -> start_time = date('Y-m-d', strtotime($order -> start_time));

        $order -> end_time = date('Y-m-d', strtotime($order -> end_time));

        Arr::except($order, ['trade']);

        return $order -> toArray();
    }

    public static function cancelOrder(int $order_id) : bool
    {
        $order = SwtPledgeOrder::find($order_id);

        if(!$order) {
            self::$error = __("order.order_not_found");
            return false;
        }

        if($order -> status != 0) {
            self::$error = __("order.status_not_cancel");
            return false;
        }

        $trade_info = SwtTradeAddress::find($order->trade_id);

        if(!$trade_info){
            self::$error = __("account.address_not_found");
            return false;
        }

        DB::beginTransaction();
        try {
            $trade_info -> lock_total = bcsub($trade_info -> lock_total, $order -> total, 5);

            $trade_info -> total = bcadd($order -> total, $trade_info -> total, 5);

            $res = $trade_info -> save();

            if(!$res) {
                throw new \Exception(__("coin-type.update_balance_error"));
            }

            $order -> status = 4;

            $res = $order -> save();

            if(!$res) {
                throw new \Exception(__("order.status_update_error"));
            }

            SwtTradeAccountLog::create([
                "address_id"    => $trade_info -> id,
                "user_id"       => $order -> user_id,
                "source_type"   => 4,
                "source_id"     => $order -> id,
                "text"          => "{cancel_pledge_add}" . $order -> total,
                "change_type"   => 0,
                'change_amount' => $order -> total
            ]);

            Db::commit();
            return true;
        } catch (\Exception $e) {
            Db::rollback();
            self::$error = $e->getMessage();
            return false;
        }
    }

    public static function getRedeemPledgeInfo(int $order_id) : array | bool
    {
        $order = SwtPledgeOrder::with(['user', 'rule','pledge']) -> find($order_id);

        if(!$order) {
            self::$error = __("order.order_not_found");
            return false;
        }

        if($order -> status !== 1) {
            self::$error = __("order.status_not_redeem");
            return false;
        }

        $redeem_rule = $order -> rule -> redeem_rule;

        if(!$redeem_rule) {
            return [
                "need_ask" => false,
            ];
        } else {
            $redeem_rule = collect(json_decode($redeem_rule, true));
        }

        $startDate = Carbon::parse($order -> start_time);

        $endDate = Carbon::parse();

        $days_difference = $startDate -> diffInDays($endDate);

        $rule = $redeem_rule -> filter(fn($item) => $item['day'] <= $days_difference) -> sortByDesc('day') -> first();

        if(!$rule) {
            return [
                "need_ask" => false,
            ];
        }

        $arr = [
            "need_ask"    => true,
            "lock_year"   => ceil($order -> lock_day / 365),
            "effect_day"  => $days_difference,
            "redeem_rate" => (float) $rule['rate'],
            "redeem_fee"  => (float) bcmul($order -> total , ($rule['rate'] / 100), 5),
            "user_phone"  => $order -> user -> phone
        ];

        return $arr;
    }

    public static function redeemPledgeOrder(int $order_id, array $redeem_info) : bool
    {
        $order = SwtPledgeOrder::find($order_id);

        $trade_info = SwtTradeAddress::find($order->trade_id);

        if(!$trade_info){
            self::$error = __("account.address_not_found");
            return false;
        }

        $total = $order -> total + $order -> income_amount;

        DB::beginTransaction();
        try {
            $trade_info -> lock_total = bcsub($trade_info -> lock_total, $total, 5);

            $trade_info -> total = bcadd($total, $trade_info -> total - $redeem_info['redeem_fee'], 5) ;

            $res = $trade_info -> save();

            if(!$res) {
                throw new \Exception(__("coin-type.update_balance_error"));
            }

            $order -> status = 2;

            $res = $order -> save();

            if(!$res) {
                throw new \Exception(__("order.status_update_error"));
            }

            SwtTradeAccountLog::create([
                "address_id"    => $trade_info -> id,
                "user_id"       => $order -> user_id,
                "source_type"   => 4,
                "source_id"     => $order -> id,
                "text"          => "{redeem_pledge_add}" . $total .'，{fee}'.$redeem_info['redeem_fee'],
                "change_type"   => 0,
                'change_amount' => $total - $redeem_info['redeem_fee']
            ]);

            Db::commit();
            return true;
        } catch (\Exception $e) {
            Db::rollback();
            self::$error = $e->getMessage();
            return false;
        }
    }
}
